﻿#region copyright info
//------------------------------------------------------------------------------
// <copyright company="ChaosStudio">
//     Copyright (c) 2002-2010 巧思工作室.  All rights reserved.
//     Contact:		MSN:zhouyu@cszi.com , QQ:478779122
//		Link:				http://www.69sea.com http://www.cszi.com
// </copyright>
//------------------------------------------------------------------------------
#endregion
using System;
using System.Threading;
using log4net;

namespace CS.WinService
{
    /// <summary>
    ///  抽象的单一任务驱动。
    ///  ver:0.3.1
    ///  <para>具体任务必须继承自本类，每个任务单独一个计时器。</para>
    /// <para>PathDate用于补全操作</para>
    /// </summary>
    /// 
    /// <description class = "CS.WinService.JobProvider">
    ///   2010-3-10 , zhouyu , 使用委托调用子类的业务
    ///   2010-4-20 , zhouyu , 配置序列化成对象重每任务在一定时间内置于线程池中执行
    /// </description>
    /// 
    /// <history>
    ///   2010-2-26 16:06:39 , zhouyu ,  创建	     
    ///  </history>
    public abstract class TaskProvider : IDisposable
    {

        #region 变量 属性

        /// <summary>
        /// 程序运行日志
        /// </summary>
        protected readonly ILog LogRun = LogManager.GetLogger(typeof(TaskProvider));
        /// <summary>
        /// 保存运行状态时的互斥体，共享变量，全局唯一
        /// </summary>
        protected static readonly Mutex SaveMutex = new Mutex(false);
        /// <summary>
        /// 工作互斥体
        /// </summary>
        protected static readonly Mutex WorkingMutex = new Mutex(false);

        /// <summary>
        /// 任务信息
        /// </summary>
        public TaskInfo Task { get; set; }

        /// <summary>
        /// 共公资源配置信息引用
        /// </summary>
        public ResourceCollection Resources { get; set; }

        /// <summary>
        /// 默认的扩展
        /// </summary>
        protected ExtendInfo Extend { get; set; }

        ///// <summary>
        ///// 宿主服务状态
        ///// </summary>
        //public ServiceStatusType ServiceStatus { get; set; }


        /// <summary>
        /// 运行状态配置的引用
        /// 用来保存运行状态的
        /// </summary>
        public ExecutionConfig ExecutionConfig { get; set; }

        /// <summary>
        /// 任务工作
        /// 用以执行本任务的。
        /// Todo:有必要在子类中访问吗？
        /// </summary>
        protected Timer TaskWorker { get; set; }


        /// <summary>
        /// 补全时间，补全类型任务执行后要更新PatchDate的值
        /// Note:1. 补全任务执行时更新的补全时间，必须从执行触发时间的上一个时间里开始，如10-15号要补全从10-1号的数据，那么任务执行完10-1号任务后该值为10-2，当前补全
        /// Note:2. 配置里的IsPath必须为true，否则不执行补全功能。
        /// </summary>
        public virtual DateTime? PathDate { get; set; }

        /// <summary>
        /// 正在运行的次数
        /// </summary>
        private int _runTimes;
        /// <summary>
        /// 是否正在回调中
        /// </summary>
        private bool _isCallBacking;

        /// <summary>
        /// 停止的委托
        /// </summary>
        private Action _stopTask;

        #endregion


        #region 构造

        protected TaskProvider()
        {
            WorkHandler = () => Work();      //附加该委托即可
            _stopTask = ( delegate {});    //默认给一个空的方法
        }


        #endregion


        #region 方法


        /// <summary>
        /// 载入扩展配置信息
        /// <para>自定义了新的扩展配置时一定要重写该方法</para>
        /// </summary>
        public virtual void LoadExtendConfig()
        {
            if (Extend == null)
                Extend = Task.GetExtend<ExtendInfo>();
        }


        /// <summary>
        /// 工作委托[返回是否执行]
        /// </summary>
        protected Func<Result> WorkHandler;
        /// <summary>
        /// 工作运行
        /// Note:两次间隔有100豪秒左右的误差
        /// </summary>
        public virtual void Working()
        {
            try
            {
                WorkingMutex.WaitOne();   //阻塞当前线程
                TaskWorker.Change(Timeout.Infinite, Timeout.Infinite); //暂停计时。
                var now = SystemTime.Now();
                _runTimes++;
                //执行今日任务
                LogRun.InfoFormat("◇任务[{0}:{1}]第{2}次执行开始。[{3:u}]", Task.Job.Id, Task.Job.Name, _runTimes, now);
                var b = SystemTime.Now();
                var val = WorkHandler();  //同步委托，任务执行[可能较耗时]
                var e = SystemTime.Now() - b;
                LogRun.InfoFormat("执行结果[{0} : {1}]，耗时:{2}", val.Stamp, val.Message, e);

                //Note:工作完成后的状态处理
                //Note:注意，这里的错误次数实际上是执行失败的次数

                Task.Job.Execution.LastRun = now;
                if ((val.Stamp & ResultType.Failed) == ResultType.Failed)
                {
                    var sleepInterval = ((TimeSpan)Task.WorkSetting.SleepInterval);
                    Task.Job.Execution.SleepTimes++;
                    LogRun.DebugFormat("状态更新,休眠次数++ ，准备[{0}]后再次执行", sleepInterval);
                    TaskWorker.Change(sleepInterval, TimeSpan.FromMilliseconds(-1));
                }
                if ((val.Stamp & ResultType.Succeed) == ResultType.Succeed)
                {
                    if (Task.WorkSetting.Interval.Times == 0) Task.Job.Execution.LastSucceedRun = now;   //Note:此时补全功能失效
                    var okInterval = ((TimeSpan)Task.WorkSetting.Interval);
                    Task.Job.Execution.RunTimes++;
                    LogRun.DebugFormat("状态更新,正确次数++ ，准备[{0}]后再次执行", okInterval);
                    TaskWorker.Change(okInterval, TimeSpan.FromMilliseconds(-1));  //Note:更改计时器约50多毫秒
                }
                SaveExecution();
                LogRun.InfoFormat("◆任务[{0}:{1}]第{2}次执行结束。", Task.Job.Id, Task.Job.Name, _runTimes);

                #region 根据任务配置做出相应动作


                //本次任务已完成,Note:只有本次任务达到所设条件才算是正常完成，正常完成后才更新最后成功完成的时间。
                if ((Task.Job.Execution.RunTimes >= Task.WorkSetting.Interval.Times && Task.WorkSetting.Interval.Times > 0) || (val.Stamp & ResultType.Finished) == ResultType.Finished)
                {
                    Task.Job.Execution.LastSucceedRun = PathDate != null ? PathDate.Value : now;   //Note:可自动补全点
                    Task.Job.Execution.Status = TaskStatusType.TodayComplete;
                    Stop(); //停止本任务
                    LogRun.InfoFormat("■任务[{0}:{1}]今日({2:u})完成。■", Task.Job.Id, Task.Job.Name, Task.Job.Execution.LastSucceedRun);
                    return;
                }

                //根据设定，一旦有错误发生。立即停止
                if (Task.Job.Execution.SleepTimes > 0 && Task.WorkSetting.ErrorWay == ErrorWayType.Stop)
                {
                    Task.Job.Execution.Status = TaskStatusType.TodayNotComplete;
                    Stop(); //停止本任务
                    LogRun.InfoFormat("▲任务[{0}:{1}]根据设定Stop，发生了错误一次，现已停止。▲", Task.Job.Id, Task.Job.Name);
                    return;
                }

                //根据设定，有错误时。休眠超期后停止
                if (Task.Job.Execution.SleepTimes >= Task.WorkSetting.SleepInterval.Times && Task.WorkSetting.SleepInterval.Times > 0 && Task.WorkSetting.ErrorWay == ErrorWayType.Sleep)
                {
                    Task.Job.Execution.Status = TaskStatusType.TodayNotComplete;
                    Stop(); //停止本任务
                    LogRun.InfoFormat("▲任务[{0}:{1}]根据设定Sleep，发生了错误{2}次，现已停止。▲", Task.Job.Id, Task.Job.Name, Task.Job.Execution.SleepTimes);
                    return;
                }

                #endregion

            }
            catch (Exception ex)
            {
                //Note:异常发生后停止该任务，不管任何原因
                LogRun.Error(string.Format("任务[{0}:{1}]执行异常，停止执行。", Task.Job.Id, Task.Job.Name), ex);
                Stop();
                throw;
            }
            finally
            {
                WorkingMutex.ReleaseMutex();
            }
        }

        /// <summary>
        /// 线程回调
        /// </summary>
        void TimerCallback(object state)
        {
            lock (this)
            {
                _isCallBacking = true;

                if (TaskWorker == null)
                {
                    LogRun.DebugFormat("任务[{0}:{1}]回调时回调计时器已为null，该情况为更改配置后引发的不稳定性。本次回调中断。", Task.Job.Id, Task.Job.Name);
                    return;
                }

                //LogRun.DebugFormat("任务[{0}:{1}]内部线程回调", Task.Job.Id, Task.Job.Name);
                Working();
                
                _isCallBacking = false;
                
                //可能的停止委托
                _stopTask();
                
            }
        }

        /// <summary>
        /// 运行状态保存
        /// </summary>
        public void SaveExecution()
        {
            SaveMutex.WaitOne();
            ExecutionConfig.Save();
            SaveMutex.ReleaseMutex();
        }

        /// <summary>
        /// 任务入口，继承类必须实现。
        /// </summary>
        /// <returns>
        /// 执行结果，参见：<see cref="Result"/>
        /// </returns>
        protected abstract Result Work();



        #endregion


        #region 任务驱动器功能

        /// <summary>
        /// 启动
        /// </summary>
        public void Start()
        {
            //计时器已存在，说明正在运行，不用再次启动
            if (TaskWorker != null)
            {
                LogRun.DebugFormat("任务[{0}:{1}]计时器存在，不用重复启动，中断本次启动。", Task.Job.Id, Task.Job.Name);
                return;
            }

            //校验 计算启动时间。
            var launchTime = Task.CalculateLaunchTime();   //计算启动时间
            if (launchTime == null)
            {
                LogRun.DebugFormat("任务[{0}:{1}]启动时间为null，中断启动。", Task.Job.Id, Task.Job.Name);
                return;
            }


            //计算下次启动间隔
            var now = SystemTime.Now();

            //如果为补全型任务 且启动时间小于当前时间 则附上本次要补全的日期
            if (Task.Job.IsPatch && launchTime.Value < now)
            {
                PathDate = launchTime;
            }

            var nextInterval = (int)(launchTime.Value.Subtract(now).TotalMilliseconds);
            if (nextInterval <= 0 || (launchTime >= now && Task.WorkSetting.Interval.Times == 0) /*Note:修正无限运行次数的任务*/ )
            {
                nextInterval = Task.WorkSetting.DelaySecond * 1000;
            }

            //Note:启动时 只有当任务执行完成，或是无限任务的还会归0，否则接着上次的运行
            if (Task.Job.Execution.RunTimes >= Task.WorkSetting.Interval.Times || Task.WorkSetting.Interval.Times == 0)
            {
                Task.Job.Execution.RunTimes = 0;
                Task.Job.Execution.SleepTimes = 0;
            }
            Task.Job.Execution.Status = TaskStatusType.Runing;
            TaskWorker = new Timer(TimerCallback, null, nextInterval /*第一次延时调用*/, Timeout.Infinite /*Note:回调时会更改调用延时，此周期设为无限*/);
            LogRun.DebugFormat("任务[{0}:{1}]启动成功，将于:{2}秒后运行", Task.Job.Id, Task.Job.Name, nextInterval / 1000.00);
        }

        
        
        /// <summary>
        /// 停止
        /// </summary>
        public void Stop()
        {
            if (TaskWorker == null) return;
            //任务回调结束后才可以结束
            if (_isCallBacking)
            {
                LogRun.DebugFormat("任务[{0}:{1}]正在回调中，停止方法附加到任务完成后的委托上。",Task.Job.Id, Task.Job.Name);
                _stopTask = StopTask; //使用委托来处理
            }
            else
            {
                StopTask();
            }
        }
      
        private void StopTask()
        {
            TaskWorker.Change(Timeout.Infinite, Timeout.Infinite);     //禁止再次回调

            Task.Job.Execution.Status = Task.Job.Execution.Status | TaskStatusType.Stop;
            SaveExecution(); //先保存一次状态

            TaskWorker.Dispose();
            TaskWorker = null;  //干掉引用
            LogRun.DebugFormat("任务[{0}:{1}]停止，计时器已释放。", Task.Job.Id, Task.Job.Name);
        }

        /// <summary>
        /// 暂停
        /// </summary>
        public void Pause()
        {
            if (TaskWorker == null) return;
            Task.Job.Execution.Status = TaskStatusType.Pasuse;
            SaveExecution(); //先保存一次状态

            TaskWorker.Change(Timeout.Infinite, Timeout.Infinite);     //禁止再次回调
            Task.Job.Execution.Status = TaskStatusType.Pasuse;
            LogRun.DebugFormat("任务[{0}:{1}]暂停，计时器已暂停。(最后一次回调的任务可能还在执行)", Task.Job.Id, Task.Job.Name);
        }

        /// <summary>
        /// 恢复
        /// </summary>
        public void Resume()
        {
            if (TaskWorker == null) return;
            Task.Job.Execution.Status = TaskStatusType.Runing;
            SaveExecution(); //先保存一次状态

            TaskWorker.Change(Task.WorkSetting.DelaySecond * 1000 /*第一次延时调用*/, Timeout.Infinite);     //重启计时器
            Task.Job.Execution.Status = TaskStatusType.Runing;
            LogRun.DebugFormat("任务[{0}:{1}]暂停，计时器已恢复。(最后一次回调的任务可能还在执行)", Task.Job.Id, Task.Job.Name);
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            try
            {
                Stop();
            }
            catch (Exception ex)
            {
                LogRun.Error("释放资源时发生异常。", ex);
                throw;
            }
        }

        #endregion


    }


}